﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Collections.ObjectModel;
using psafth.AMB;
using psafth.RCTiming.Common;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity.Validation;
using System.Windows.Threading;
using System.Threading;

namespace psafth.RCTiming.Main
{
    public class DBContext : DbContext
    {
        public DbSet<Club> Clubs { get; set; }
        public DbSet<Driver> Drivers { get; set; }
        public DbSet<Transponder> Transponders { get; set; }
        public DbSet<Event> Events { get; set; }
        public DbSet<Heat> Heats { get; set; }
        public DbSet<RaceClass> RaceClasses { get; set; }
    }

    [Serializable]
    public class Club
    {
        [Key]
        public int Id { get; set; }
        public string FullName { get; set; }                            // Hanöbuktens MBK
        public string ShortName { get; set; }                           // HMBK
        public string Country { get; set; }                             // Sweden
        public string LicencePrefix { get; set; }                       // S8-

        public virtual List<Driver> Drivers { get; set; }

        public Club()
        {
            Drivers = new List<Driver>();
        }
    }

    [Serializable]
    public class Driver
    {
        [Key]
        public int Id { get; set; }
        public string FirstName { get; set; }                           // Fredrik
        public string LastName { get; set; }                            // Svensson   
        public string EmailAddress { get; set; }                        // fredrik_s89@hotmail.com
        public string LicenceNumber { get; set; }                       // S8-49
        public string Country { get; set; }                             // Sweden

        public string FullName
        {
            get
            {
                return string.Format("{0} {1}", FirstName, LastName);
            }
        }

        public string FullNameWithLicence
        {
            get
            {
                return string.Format("{0} {1} [{2}]", FirstName, LastName, LicenceNumber);
            }
        }

        public Driver()
        {
            Transponders = new List<Transponder>();
        }

        public virtual Club Club { get; set; }
        public virtual List<Transponder> Transponders { get; set; }
    }

    [Serializable]
    public class Transponder
    {
        [Key]
        public int Id { get; set; }

        public int TransponderId { get; set; }                         // 4777678

        public virtual Driver Driver { get; set; }
    }

    public class Event
    {
        [Key]
        public int Id { get; set; }
        public string EventName { get; set; }                           // FSR-V SM3 Lida
        public DateTime StartDate { get; set; }                         // 2013-05-23
        public DateTime EndDate { get; set; }                           // 2013-05-24
        public string Organizer { get; set; }

        public virtual ObservableCollectionExt<Heat> Heats { get; set; }
        public virtual ObservableCollectionExt<EventParticipant> Participants { get; set; }

        public Event()
        {
            Heats = new ObservableCollectionExt<Heat>();
            Participants = new ObservableCollectionExt<EventParticipant>();
        }
    }

    [Serializable]
    public class EventParticipant
    {
        public EventParticipant()
        {
        }

        public EventParticipant(Driver d)
        {
            this.DriverId = d.Id;
        }

        [Key]
        public int Id { get; set; }

        public int RaceClassId { get; set; }

        public int DriverId { get; set; }

        public int? TransponderId { get; set; }

        public virtual Driver Driver
        {
            get
            {
                using (DBContext db = new DBContext())
                {
                    return db.Drivers.FirstOrDefault(d => d.Id == DriverId);
                }
            }
        }

        public virtual RaceClass RaceClass
        {
            get
            {
                RaceClass rc = null;

                DatabaseHelper.DoWithDB<DBContext>(db =>
                {
                    rc = db.RaceClasses.FirstOrDefault(r => r.Id == RaceClassId);
                });

                return rc;
            }
        }
    }

    [Serializable]
    public class Heat
    {
        [Key]
        public int Id { get; set; }
        public string Name { get; set; }
        public int Duration { get; set; }
        public string Elapsed { get; set; }
        public bool IsRunning { get; set; }
        public int Group { get; set; }

        public virtual Event Event { get; set; }
        public virtual RaceClass RaceClass { get; set; }
        public virtual ObservableCollectionExt<HeatCompetitor> HeatCompetitors { get; set; }
    }

    [Serializable]
    public class HeatCompetitor : INotifyPropertyChanged
    {
        [Key]
        public int Id { get; set; }

        private ObservableCollectionExt<Lap> _laps;
        private double _finishTime;
        private int? _position;
        private string _estimatedResult;
        private bool _isFinished;

        public int? StartNumber { get; set; }

        public HeatCompetitor()
        {
            Laps = new ObservableCollectionExt<Lap>();
        }

        public HeatCompetitor(Driver d, int? t)
        {
            Laps = new ObservableCollectionExt<Lap>();
            TransponderId = t;

            this.FullName = d.FullName;
            this.FirstName = d.FirstName;
            this.LastName = d.LastName;

            DatabaseHelper.DoWithDB<DBContext>(db =>
            {
                Club club = null;

                Driver driver = db.Drivers.FirstOrDefault(dr => dr.Id == d.Id);

                if (driver != null)
                    club = driver.Club;

                if (club != null)
                    this.ClubName = club.FullName;
            });

            this.LicenceNumber = d.LicenceNumber;
        }

        public string FullName
        {
            get;
            set;
        }

        public string FirstName
        {
            get;
            set;
        }

        public string LastName
        {
            get;
            set;
        }

        public string ClubName
        {
            get;
            set;
        }

        public string LicenceNumber
        {
            get;
            set;
        }

        public bool IsFinished
        {
            get
            {
                return _isFinished;
            }
            set
            {
                _isFinished = value;
                RaisePropertyChanged("IsFinished");
            }
        }

        public int? Position
        {
            get
            {
                return _position;
            }
            set
            {
                _position = value;
                RaisePropertyChanged("Position");
            }
        }

        public string EstimatedResult
        {
            get
            {
                return _estimatedResult;
            }
            set
            {
                _estimatedResult = value;
                RaisePropertyChanged("EstimatedResult");
            }
        }

        public double FinishTime
        {
            get
            {
                return _finishTime;
            }
            set
            {
                _finishTime = value;
                RaisePropertyChanged("FinishTime");
                RaisePropertyChanged("StringNumberOfLaps");
            }
        }

        public ObservableCollectionExt<Lap> Laps
        {
            get
            {
                return _laps;
            }
            set
            {
                _laps = value;
                RaisePropertyChanged("Laps");
            }
        }

        public string StringNumberOfLaps
        {
            get
            {
                if (FinishTime != 0)
                    return string.Format("{0}v {1}s", Laps.Count(l => l.IsValid), AMBCalculator.TrimDouble(FinishTime, 3));

                return string.Format("{0}", Laps.Count(l => l.IsValid));
            }
        }


        public int NumberOfLaps
        {
            get
            {
                return Laps.Count(l => l.IsValid);
            }
        }

        public double BestLapTime
        {
            get
            {
                try
                {
                    if (Laps.Count(l => l.IsValid) > 0)
                    {
                        List<Lap> l = Laps.OrderBy(s => s.LapTime).ToList();

                        //Lap Best = Laps.FirstOrDefault(la => la.LapTime == l.Min(s => s.LapTime) && la.IsValid);

                        double bestLaptime = Laps.Where(la => la.IsValid).Min(la => la.LapTime);

                        if (bestLaptime > 0)
                            return AMBCalculator.TrimDouble(bestLaptime, 3);

                        return 0.0;
                    }
                    else
                    {
                        return 0.0;
                    }
                }
                catch (Exception)
                {
#if DEBUG
                    throw;
#endif
                    return -2;
                }
            }
        }

        public double AverageLapTime
        {
            get
            {
                double totalTime = 0.0;

                foreach (Lap l in Laps.Where(la => la.IsValid))
                {
                    totalTime += l.LapTime;
                }

                return AMBCalculator.TrimDouble(totalTime / (Laps.Count(l => l.IsValid)), 3);
            }
        }

        public void InvalidateLastLap(Lap.InvalidationType type)
        {
            PreviousLap.InvalidReason = type;
            PreviousLap.IsValid = false;

            RaisePropertyChanged("NumberOfLaps");
            RaisePropertyChanged("StringNumberOfLaps");
            RaisePropertyChanged("FinishTime");
            RaisePropertyChanged("Laps");
            RaisePropertyChanged("PreviousLap");
            RaisePropertyChanged("StartNumber");
            RaisePropertyChanged("Driver");
            RaisePropertyChanged("BestLapTime");
            RaisePropertyChanged("AverageLapTime");
            RaisePropertyChanged("Transponder");
            RaisePropertyChanged("SignalStrength");
        }

        public void AddLap(DateTime startTime, DateTime timeStamp, double signalStrength, double voltage, double hits)
        {
            AddLap(startTime, timeStamp, signalStrength, voltage, hits, false);
        }

        public void AddLap(DateTime startTime, DateTime timeStamp, double signalStrength, double voltage, double hits, bool isManual)
        {
            DateTime prevPassing;

            if (PreviousLap != null)
            {
                prevPassing = PreviousLap.PassingTime;
            }
            else
            {
                prevPassing = startTime;
            }

            TimeSpan lapTime = timeStamp.Subtract(prevPassing);

            // If the laptime is too short, reject it.
            if (lapTime < new TimeSpan(0, 0, Properties.Settings.Default.MinimumLapTime))
                return;

            Lap lap = new Lap(lapTime.TotalSeconds, timeStamp, signalStrength, voltage, hits, isManual);

            //AsyncHelper.EnsureUIThread(() =>
            //{
            this.Laps.Add(lap);

            RaisePropertyChanged("FullName");
            RaisePropertyChanged("Position");
            RaisePropertyChanged("NumberOfLaps");
            RaisePropertyChanged("StringNumberOfLaps");
            RaisePropertyChanged("FinishTime");
            RaisePropertyChanged("Laps");
            RaisePropertyChanged("PreviousLap");
            RaisePropertyChanged("StartNumber");
            RaisePropertyChanged("Driver");
            RaisePropertyChanged("BestLapTime");
            RaisePropertyChanged("AverageLapTime");
            RaisePropertyChanged("Transponder");
            RaisePropertyChanged("SignalStrength");
            RaisePropertyChanged("Hits");
            //});

        }

        public Lap PreviousLap
        {
            get
            {
                try
                {
                    return Laps.Last(l => l.IsValid);
                }
                catch (Exception)
                {
                    return null;
                }
            }
        }

        //public int DriverId { get; set; }

        //public Driver Driver
        //{
        //    get
        //    {
        //        Driver driver = null;

        //        DatabaseHelper.DoWithDB<DBContext>(db =>
        //        {
        //            driver = db.Drivers.FirstOrDefault(d => d.Id == this.DriverId);
        //        });

        //        return driver;
        //    }
        //}

        public int? TransponderId { get; set; }

        #region *** INotifyPropertyChanged Members and Invoker ***
        [field: NonSerialized]
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void RaisePropertyChanged(string propertyName)
        {
            var temp = PropertyChanged;
            if (temp != null)
                temp(this, new PropertyChangedEventArgs(propertyName));

        }
        #endregion

    }

    public class RaceClass
    {
        [Key]
        public int Id { get; set; }
        public string Name { get; set; }
    }
}
